package com.applcn.user.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.applcn.core.base.Result;
import com.applcn.user.constant.AuthRangeEnum;
import com.applcn.user.constant.MenuTypeEnum;
import com.applcn.user.dto.merchant.*;
import com.applcn.user.repo.entity.MenuEntity;
import com.applcn.user.repo.entity.RoleEntity;
import com.applcn.user.repo.entity.RoleMenuEntity;
import com.applcn.user.repo.repository.MenuRepository;
import com.applcn.user.repo.repository.RoleMenuRepository;
import com.applcn.user.repo.repository.RoleRepository;
import com.applcn.user.threadlocal.UserRequestContextHolder;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * com.applcn.user.service
 * Description:
 * TODO 本页店铺id后续全部放到上下文，并且在aop中鉴权
 *
 * @author: 大妖怪
 * @date: 2020-03-09 00:27
 */
@Service
@RequiredArgsConstructor
public class MenuService {

    private final RoleRepository roleRepository;
    private final RoleMenuRepository roleMenuRepository;
    private final MenuRepository menuRepository;
    private final CommonService commonService;

    /**
     * 获取动态路由
     *
     * @param authRange
     * @return
     */
    public Result getCurrentUserMenuList(AuthRangeEnum authRange) {
        Long userId = UserRequestContextHolder.getUserId();
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(userId, "当前用户未登录");

        if (shopId == null) {
            return Result.ok();
        }

        List<NavigationRespDTO> navigation = getMenuListByUserId(shopId, userId, authRange);
        return Result.ok().putData(navigation);
    }

    /**
     * 根据用户名，范围，店铺，获取用户菜单列表
     *
     * @param shopId
     * @param userId
     * @param authRange
     * @return
     */
    private List<NavigationRespDTO> getMenuListByUserId(Long shopId, Long userId, AuthRangeEnum authRange) {
        List<RoleEntity> roleEntityList = roleRepository.getRoleByUserIdAndAuthRange(shopId, userId, authRange);
        List<Long> roleIdList = roleEntityList.stream().map(RoleEntity::getId).collect(Collectors.toList());
        Set<RoleMenuEntity> roleMenuEntityList = roleMenuRepository.getRoleMenuListByRoleIdList(roleIdList);
        List<Long> menuIdList = roleMenuEntityList.stream().map(RoleMenuEntity::getMenuId).collect(Collectors.toList());
        List<MenuEntity> menuEntityList = menuRepository.listByIds(menuIdList);
        return getMenuList(0L, menuEntityList);
    }

    /**
     * 根据父菜单id获取菜单列表
     *
     * @param parentId
     * @return
     */
    public Result menuList(Long parentId) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请先选择店铺！");
        List<MenuEntity> menuEntityList = menuRepository.menuList(shopId, parentId, null);
        List<MenuListRespDTO> navigation = new ArrayList<>();
        for (MenuEntity menuEntity : menuEntityList) {
            MenuListRespDTO menuListRespDTO = new MenuListRespDTO();
            BeanUtils.copyProperties(menuEntity, menuListRespDTO);
            Integer countChildren = menuRepository.countChildren(menuEntity.getId(), null);
            menuListRespDTO.setMenuType(menuEntity.getMenuType().getCode());
            menuListRespDTO.setHasChildren(countChildren > 0);
            navigation.add(menuListRespDTO);
        }
        return Result.ok().putData(navigation);
    }

    /**
     * 添加/修改菜单
     *
     * @param dto
     * @return
     */
    public Result saveOrChangeMenu(SaveOrChangeMenuReqDTO dto) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请先选择店铺！");
        commonService.checkShop(shopId);
        MenuEntity menuEntity;
        if (dto.getParentId() != null && dto.getParentId() != 0) {
            MenuEntity parent = menuRepository.getById(dto.getParentId());
            Assert.notNull(parent, "父菜单不存在！");
        }
        MenuTypeEnum menuTypeEnum = MenuTypeEnum.checkCode(dto.getMenuType());
        if (dto.getId() != null) {
            menuEntity = menuRepository.getById(dto.getId());
            Assert.notNull(menuEntity, "菜单不存在！");
        } else {
            Assert.notNull(dto.getParentId(), "请传入父菜单id！");
            Assert.notBlank(dto.getName(), "菜单名称不能为空！");
            Assert.notNull(menuTypeEnum, "请传入正确的菜单类型！");
            Assert.notNull(dto.getSort(), "排序值不能为空！");
            menuEntity = new MenuEntity();
            menuEntity.setShopId(shopId);
        }
        if (dto.getParentId() != null) {
            menuEntity.setParentId(dto.getParentId());
        }
        if (StrUtil.isNotBlank(dto.getName())) {
            menuEntity.setName(dto.getName());
        }
        if (menuTypeEnum != null) {
            menuEntity.setMenuType(menuTypeEnum);
        }
        if (StrUtil.isNotBlank(dto.getIcon())) {
            menuEntity.setIcon(dto.getIcon());
        }
        if (dto.getSort() != null) {
            menuEntity.setSort(dto.getSort());
        }

        menuRepository.saveOrUpdate(menuEntity);
        return Result.ok();
    }

    /**
     * 批量删除菜单
     *
     * @param dto
     * @return
     */
    public Result deleteMenuByIds(DeleteMenuReqDTO dto) {
        Assert.notEmpty(dto.getIds(), "请传入菜单id！");
        menuRepository.deleteBatch(dto.getIds(), dto.getShopId());
        return Result.ok();
    }

    /**
     * 拷贝一份shopId为空0的菜单
     *
     * @param shopId
     * @param roleId
     * @return
     */
    public void copyMenu(Long shopId, Long roleId) {
        List<MenuEntity> menuEntityList = menuRepository.listByShopId(0L);
        List<NavigationRespDTO> menuList = getMenuList(0L, menuEntityList);
        saveMenu(shopId, roleId, 0L, menuList);
    }

    /**
     * 储存目录
     *
     * @param shopId
     * @param roleId
     * @param menuList
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveMenu(Long shopId, Long roleId, Long parentId, List<NavigationRespDTO> menuList) {
        for (NavigationRespDTO item : menuList) {
            MenuEntity menuEntity = new MenuEntity();
            menuEntity.setParentId(parentId);
            menuEntity.setShopId(shopId);
            menuEntity.setName(item.getName());
            menuEntity.setMenuType(MenuTypeEnum.checkCode(item.getMenuType()));
            menuEntity.setUrl(item.getUrl());
            menuEntity.setIcon(item.getIcon());
            menuEntity.setSort(item.getSort());
            menuRepository.save(menuEntity);

            RoleMenuEntity roleMenuEntity = new RoleMenuEntity();
            roleMenuEntity.setRoleId(roleId);
            roleMenuEntity.setMenuId(menuEntity.getId());
            roleMenuRepository.save(roleMenuEntity);

            saveMenu(shopId, roleId, menuEntity.getId(), item.getList());
        }
    }

    /**
     * 递归遍历目录
     *
     * @param parentId
     * @param menuEntityList
     * @return
     */
    private List<NavigationRespDTO> getMenuList(final Long parentId, final List<MenuEntity> menuEntityList) {
        List<MenuEntity> parentList = menuEntityList.stream().filter(e -> e.getParentId().equals(parentId)).collect(Collectors.toList());
        Collections.sort(parentList);
        menuEntityList.removeAll(parentList);

        List<NavigationRespDTO> navigation = new ArrayList<>();
        for (MenuEntity menuEntity : parentList) {
            NavigationRespDTO navigationRespDTO = new NavigationRespDTO();
            navigationRespDTO.setMenuId(menuEntity.getId());
            navigationRespDTO.setName(menuEntity.getName());
            navigationRespDTO.setRoute(menuEntity.getRoute());
            navigationRespDTO.setMenuType(menuEntity.getMenuType().getCode());
            navigationRespDTO.setUrl(menuEntity.getUrl());
            navigationRespDTO.setIcon(menuEntity.getIcon());
            navigationRespDTO.setList(getMenuList(menuEntity.getId(), menuEntityList));
            navigationRespDTO.setSort(menuEntity.getSort());
            navigation.add(navigationRespDTO);
        }

        return navigation;
    }

    /**
     * 获取目录列表
     *
     * @param parentId
     * @return
     */
    public Result catalogList(Long parentId) {
        return Result.ok().putData(getCatalogList(parentId));
    }

    /**
     * 递归获取目录列表
     *
     * @param parentId
     * @return
     */
    private List<CatalogListDTO> getCatalogList(Long parentId) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请先选择店铺！");
        List<MenuEntity> menuEntityList = menuRepository.menuList(shopId, parentId, MenuTypeEnum.CATALOG);
        List<CatalogListDTO> catalogListDTOList = new ArrayList<>();
        for (MenuEntity menuEntity : menuEntityList) {
            CatalogListDTO catalogListDTO = new CatalogListDTO();
            catalogListDTO.setValue(menuEntity.getId());
            catalogListDTO.setLabel(menuEntity.getName());
            List<CatalogListDTO> children = getCatalogList(menuEntity.getId());
            if (CollectionUtil.isNotEmpty(children)) {
                catalogListDTO.setChildren(children);
            }
            catalogListDTOList.add(catalogListDTO);
        }
        return catalogListDTOList;
    }

    /**
     * 通过id获取菜单
     *
     * @param id
     * @return
     */
    public Result menuDetail(Long id) {
        MenuEntity menuEntity = menuRepository.getById(id);
        Assert.notNull(menuEntity, "菜单不存在！");
        MenuDetailDTO dto = new MenuDetailDTO();
        dto.setId(menuEntity.getId());
        dto.setName(menuEntity.getName());
        dto.setMenuType(Integer.toString(menuEntity.getMenuType().getCode()));
        dto.setSort(menuEntity.getSort());
        dto.setParentId(menuEntity.getParentId());
        dto.setIcon(menuEntity.getIcon());
        dto.setAllParentIds(CollectionUtil.reverse(getAllParentIds(menuEntity)));
        return Result.ok().putData(dto);
    }

    /**
     * 获取菜单的所有父菜单id
     *
     * @param menuEntity
     * @return
     */
    private List<Long> getAllParentIds(MenuEntity menuEntity) {
        if (menuEntity.getParentId() == 0) {
            return Lists.newArrayList(menuEntity.getId());
        }
        MenuEntity parent = menuRepository.getById(menuEntity.getParentId());
        List<Long> ids = new ArrayList<>();
        List<Long> parentIds = getAllParentIds(parent);
        ids.addAll(parentIds);
        return ids;
    }
}
